package org.jpropeller.ui.impl;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;

import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;

import org.jpropeller.ui.IconAndHTMLRenderer;

/**
 * A simple {@link TableCellRenderer} for any class, which will delegate
 * to an {@link IconAndHTMLRenderer} to give the contents of the {@link JLabel}
 * for each displayed object
 */
public class IconAndHTMLCellRenderer extends DefaultTableCellRenderer {

	private final IconAndHTMLRenderer delegate;
	private final int verticalAlignment;
	private final boolean showIcon; 
	private final boolean showHTML;
	
	private final boolean adjustRowHeight;
	
	private boolean isSelected;
	private final IconAndHTMLRenderer selectedDelegate;
	
	/**
	 * Create a renderer showing icon and html, with label aligned centrally
	 * 
	 * @param delegate		The delegate used to render to icon and/or html 
	 */
	public IconAndHTMLCellRenderer(IconAndHTMLRenderer delegate) {
		this(delegate, SwingConstants.CENTER);
	}
	
	/**
	 * Create a renderer showing icon and html, with label aligned centrally
	 * A different renderer is used when the cell is selected.
	 * @param delegate		The delegate used to render to icon and/or html 
	 * @param selectedDelegate		The delegate used to render to icon and/or html when the cell is selected.
	 */
	public IconAndHTMLCellRenderer(IconAndHTMLRenderer delegate,IconAndHTMLRenderer selectedDelegate) {
		this(delegate,selectedDelegate, SwingConstants.CENTER, true, true, true, null);
	}

	/**
	 * Create a renderer showing icon and html
	 * 
	 * @param delegate			The delegate used to render to icon and/or html
	 * @param verticalAlignment The alignment of the label used to render - one 
	 * 							of {@link SwingConstants#TOP}, {@link SwingConstants#BOTTOM}
	 * 							or {@link SwingConstants#CENTER}
	 */
	public IconAndHTMLCellRenderer(IconAndHTMLRenderer delegate, int verticalAlignment) {
		this(delegate, verticalAlignment, true, true, true, null);
	}
	
	/**
	 * Create a renderer
	 * 
	 * @param delegate			The delegate used to render to icon and/or html
	 * @param verticalAlignment The alignment of the label used to render - one 
	 * 							of {@link SwingConstants#TOP}, {@link SwingConstants#BOTTOM}
	 * 							or {@link SwingConstants#CENTER}
	 * @param showIcon			True to show icon 
	 * @param showHTML 			True to show html
	 * @param opaque 			True to render opaque
	 * @param foreground		The foreground color, null to ignore
	 */
	public IconAndHTMLCellRenderer(IconAndHTMLRenderer delegate, int verticalAlignment, boolean showIcon, boolean showHTML, boolean opaque, Color foreground) {
		this(delegate,
				null, verticalAlignment, showIcon, showHTML, opaque, foreground);
	}
	
	public IconAndHTMLCellRenderer(IconAndHTMLRenderer delegate,
			IconAndHTMLRenderer selectedDelegate, int verticalAlignment, boolean showIcon, boolean showHTML, boolean opaque, Color foreground) {
		this(delegate,selectedDelegate,verticalAlignment,showIcon,showHTML,opaque,foreground,false);
	}
	
	public IconAndHTMLCellRenderer(IconAndHTMLRenderer delegate,
			IconAndHTMLRenderer selectedDelegate, int verticalAlignment, boolean showIcon, boolean showHTML, boolean opaque, Color foreground,
			boolean adjustRowHeight) {
		super();
		setOpaque(opaque);
		this.delegate = delegate;
		this.selectedDelegate = selectedDelegate;
		this.verticalAlignment = verticalAlignment;
		this.showIcon = showIcon;
		this.showHTML = showHTML;
		this.adjustRowHeight=adjustRowHeight;
		if (foreground != null) {
			setForeground(foreground);
		}
	}
	
	public IconAndHTMLCellRenderer(IconAndHTMLRenderer delegate, boolean adjustRowHeight) {
		this(delegate,null,SwingConstants.CENTER,true,true,true,null,adjustRowHeight);
//		System.out.println("Constructor adjustRowHeight="+adjustRowHeight+" this.adjustRowHeight="+this.adjustRowHeight);
	}

	@Override
	public Component getTableCellRendererComponent(JTable table,
            Object value,
            boolean isSelected,
            boolean hasFocus,
            int row,
            int column)
	{
		this.isSelected=isSelected;
		Component ret=super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
		if(adjustRowHeight)
		{
			if(column<table.getColumnCount() && row<table.getRowCount())
			{
				int width=table.getColumnModel().getColumn(column).getWidth()-getInsets().left-getInsets().right;
				if(getIcon()!=null)
				{
					width-=getIcon().getIconWidth()+getIconTextGap();
				}
				Dimension d=getPreferredSize(this,true,width);
				if((d.height+10)>table.getRowHeight(row))
				{
					table.setRowHeight(row,(d.height+10));
				}
			}
		}
		return ret;
	}
	
    /**
     * Returns the preferred size to set a component at in order to render
     * an html string.  You can specify the size of one dimension.
     * see http://blog.nobel-joergensen.com/2009/01/18/changing-preferred-size-of-a-html-jlabel/
     * */
    private static Dimension getPreferredSize(JLabel resizer,boolean width, int prefSize) {
 
        javax.swing.text.View view = (javax.swing.text.View) resizer.getClientProperty(
                javax.swing.plaf.basic.BasicHTML.propertyKey);
        
        if(view==null)
        {
        	return new Dimension(0,0);
        }
 
        view.setSize(width?prefSize:0,width?0:prefSize);
 
        float w = view.getPreferredSpan(javax.swing.text.View.X_AXIS);
        float h = view.getPreferredSpan(javax.swing.text.View.Y_AXIS);
 
        return new Dimension((int) Math.ceil(w),
                (int) Math.ceil(h));
    }
	
	@Override
	public void setValue(Object value) {
		//If we can render, do so. Otherwise retain default behaviour.
		if (delegate.canRender(value)) {
			setIcon(showIcon ? delegate.getIcon(value) : null);
			if(isSelected && selectedDelegate!=null && selectedDelegate.canRender(value))
			{
				setText(showHTML ? "<html>" + selectedDelegate.getHTML(value) + "</html>" : "");
			}
			else
			{
				setText(showHTML ? "<html>" + delegate.getHTML(value) + "</html>" : "");
			}
		} else {
			setIcon(null);
			super.setValue(value);
		}
		setVerticalAlignment(verticalAlignment);
	}
}